﻿using System;
using System.Collections.Generic;

using DigitalQuran.Numerology;
using System.Text;


namespace DigitalQuran.Extensions
{
    public static class Int32Extension
    {
        public static int RightJoin(this int number, int otherNumber)
        {
            var digits = otherNumber.GetDigits();

            var result = number;

            foreach (var digit in digits)
            {
                result = (int)((result * 10) + digit);
            }

            return result;
        }

        public static List<int> GetDigits(this int num)
        {
            var result = new List<int>();

            if (num < 10)
                result.Add(num);
            else
                while (num > 0)
                {
                    result.Insert(0, num % 10);
                    num = num / 10;
                }

            return result;
        }

        public static int DigitCount(this int number)
        {
            int result = 1;

            while (number > 9)
            {
                result++;
                number = number / 10;
            }

            return result;
        }

        public static int DigitSum(this int number)
        {
            var sum = number % 10;
            number /= 10;

            while (number > 0)
            {
                sum += number % 10;
                number /= 10;
            }

            return sum;
        }

        public static int DigitalRoot(this int number)
        {
            var result = DigitSum(number);

            while (result / 10 != 0)
            {
                result = DigitSum(result);
            }

            return result;
        }

        public static bool IsOdd(this int number)
        {
            return number.IsOdd(2);
        }

        public static bool IsEven(this int number)
        {
            return number.IsEven(2);
        }

        public static bool IsOdd(this int number, int prime)
        {
            return ((number % prime) != 0);
        }

        public static bool IsEven(this int number, int prime)
        {
            return ((number % prime) == 0);
        }

        public static bool IsPrime(this int number)
        {
            switch (number)
            {
                case 0:
                    return false;
                case 1:
                    return Numbers.IsOnePrime;
                case 2:
                    return true;    // 2 is the only even prime
            }

            if (number % 2 == 0)    // exclude even numbers
                return false;

            var sqrt = (long)Math.Sqrt(number);
            for (int i = 3; i <= sqrt; i += 2)
            {
                if ((number % i) == 0)
                    return false;
            }
            return true;
        }

        public static bool IsAdditivePrime(this int number)
        {
            if (number.IsPrime())
                return number.DigitSum().IsPrime();

            return false;
        }

        public static bool IsPurePrime(this int number)
        {
            if (number.IsAdditivePrime())
                return number.IsPrimeDigits();

            return false;
        }

        public static bool IsComposite(this int number)
        {
            switch (number)
            {
                case 0:
                    return true;
                case 1:
                    return !Numbers.IsOnePrime;
                case 2:
                    return false;   // 2 is the only even prime
            }

            if (number % 2 == 0)    // exclude even numbers
                return true;

            var sqrt = (int)Math.Sqrt(number);
            for (int i = 3; i <= sqrt; i += 2)
            {
                if ((number % i) == 0)
                    return true;
            }

            return false;
        }

        public static bool IsPrimeDigits(this int number)
        {
            if (number < 10)
                return number.IsPrime();
            else
                while (number > 0)
                {
                    if (!number.IsPrime())
                        return false;
                    number = number / 10;
                }

            return true;
        }

        public static bool IsCompositeDigits(this int number)
        {
            while (number > 0)
            {
                if (!(number % 10).IsComposite())
                    return false;
                number = number / 10;
            }

            return true;
        }

        public static List<int> Factorize(this int number)
        {
            var result = new List<int>();

            if (number <= 2)
            {
                result.Add(number);
            }

            else // if (number > 2)
            {
                bool isFactorFound;
                // if number has a prime factor add it to factors,
                // number /= p,
                // reloop until  number == 1
                while (number != 1)
                {
                    if ((number % 2) == 0) // if even number
                    {
                        result.Add(2);
                        number /= 2;
                    }
                    else // trial divide by all primes upto sqrt(number)
                    {
                        var max = (int)(Math.Sqrt(number)) + 1;	// extra 1 for double calculation errors

                        isFactorFound = false;
                        for (int p = 3; p <= max; p += 2)		// should only use primes, but odds will do for now
                        {
                            if ((number % p) == 0)
                            {
                                isFactorFound = true;
                                result.Add(p);
                                number /= p;
                                break; // for loop, reloop while
                            }
                        }

                        // if no prime factor found the number must be prime in the first place
                        if (!isFactorFound)
                        {
                            result.Add(number);
                            break; // while loop
                        }
                    }
                }
            }
            return result;
        }

        public static string ToArabic(this int num)
        {
            var result = new StringBuilder(); //new string(Constants.ArabicDigits[num % 10], 1);

            if (num < 10)
                result.Append(Constants.ArabicDigits[num]);
            else
                while (num > 0)
                {
                    result.Insert(0, Constants.ArabicDigits[num % 10]);
                    num = num / 10;
                }

            return result.ToString();
        }        
    }

    public static class Int64Extension {
        public static List<long> Factorize(this long number) {
            var result = new List<long>();

            if (number <= 2) {
                result.Add(number);
            } else // if (number > 2)
            {
                bool isFactorFound;
                // if number has a prime factor add it to factors,
                // number /= p,
                // reloop until  number == 1
                while (number != 1) {
                    if ((number % 2) == 0) // if even number
                    {
                        result.Add(2);
                        number /= 2;
                    } else // trial divide by all primes upto sqrt(number)
                    {
                        var max = (long)(Math.Sqrt(number)) + 1;	// extra 1 for double calculation errors

                        isFactorFound = false;
                        for (int p = 3; p <= max; p += 2)		// should only use primes, but odds will do for now
                        {
                            if ((number % p) == 0) {
                                isFactorFound = true;
                                result.Add(p);
                                number /= p;
                                break; // for loop, reloop while
                            }
                        }

                        // if no prime factor found the number must be prime in the first place
                        if (!isFactorFound) {
                            result.Add(number);
                            break; // while loop
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Join the specified number on right of number
        /// </summary>
        /// <param name="number">number to join with</param>
        /// <param name="otherNumber">Number to join</param>
        /// <returns>Return a number right joined the specifed number</returns>
        public static long RightJoin(this long number, long otherNumber) {
            var digits = otherNumber.GetDigits();

            var result = number;

            foreach (var digit in digits)
                result = (result * 10) + digit;

            return result;
        }

        /// <summary>
        /// Gets digits of Number
        /// </summary>
        /// <param name="num">Number whose digit to get</param>
        /// <returns>List of digits</returns>
        public static List<byte> GetDigits(this long num) {
            var result = new List<byte>(1);

            if (num < 10)
                result.Add((byte)num);
            else
                while (num > 0) {
                    result.Insert(0, (byte)(num % 10));
                    num = num / 10;
                }

            return result;
        }

        /// <summary>
        /// Counts the digits in number
        /// </summary>
        /// <param name="number">Number whose digit to count</param>
        /// <returns>Returns number of digits</returns>
        public static int DigitCount(this long number) {
            int result = 1;

            while (number > 9) {
                result++;
                number = number / 10;
            }

            return result;
        }

        /// <summary>
        /// Calcuate sum of digits
        /// </summary>
        /// <param name="number">Number whose digit sum to find</param>
        /// <returns>Returns Sum of digits</returns>
        public static long DigitSum(this long number) {
            var sum = number % 10;
            number /= 10;

            while (number > 0) {
                sum += number % 10;
                number /= 10;
            }

            return sum;
        }

        /// <summary>
        /// Calculate Digital Root of Number
        /// </summary>
        /// <param name="number">Number whose digital root to calculate</param>
        /// <returns>Returns digital root of number</returns>
        public static long DigitalRoot(this long number) {
            var result = DigitSum(number);

            while (result / 10 != 0) {
                result = DigitSum(result);
            }

            return result;
        }

        /// <summary>
        /// Checks whether number is odd or not
        /// </summary>
        /// <param name="number">Number to check</param>
        /// <returns>Returns true if number is odd</returns>
        public static bool IsOdd(this long number) {
            return number.IsOdd(2);
        }

        /// <summary>
        /// Checks whether number is even or not
        /// </summary>
        /// <param name="number">Number to check</param>
        /// <returns>Returns true if number is even</returns>
        public static bool IsEven(this long number) {
            return number.IsEven(2);
        }

        public static bool IsOdd(this long number, long prime) {
            return ((number % prime) != 0);
        }

        public static bool IsEven(this long number, long prime) {
            return ((number % prime) == 0);
        }

        /// <summary>
        /// Checks wether number is prime or not
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static bool IsPrime(this long number) {
            switch (number) {
                case 0:
                    return false;
                case 1:
                    return Numbers.IsOnePrime;
                case 2:
                    return true;    // 2 is the only even prime
            }

            if (number % 2 == 0)    // exclude even numbers
                return false;

            var sqrt = (long)Math.Sqrt(number);
            for (int i = 3; i <= sqrt; i += 2) {
                if ((number % i) == 0)
                    return false;
            }
            return true;
        }

        public static bool IsAdditivePrime(this long number) {
            if (number.IsPrime())
                return number.DigitSum().IsPrime();

            return false;
        }

        public static bool IsPurePrime(this long number) {
            if (number.IsAdditivePrime())
                return number.IsPrimeDigits();

            return false;
        }

        public static bool IsComposite(this long number) {
            switch (number) {
                case 0:
                    return true;
                case 1:
                    return !Numbers.IsOnePrime;
                case 2:
                    return false;   // 2 is the only even prime
            }

            if (number % 2 == 0)    // exclude even numbers
                return true;

            var sqrt = (long)Math.Sqrt(number);
            for (int i = 3; i <= sqrt; i += 2) {
                if ((number % i) == 0)
                    return true;
            }

            return false;
        }

        public static bool IsAdditiveComposite(this long number) {
            if (number.IsComposite())
                return number.DigitSum().IsComposite();

            return false;
        }

        public static bool IsPureComposite(this long number) {
            if (number.IsAdditiveComposite())
                return number.IsCompositeDigits();

            return false;
        }

        public static bool IsPrimeDigits(this long number) {
            if (number < 10)
                return number.IsPrime();
            else
                while (number > 0) {
                    if (!number.IsPrime())
                        return false;
                    number = number / 10;
                }

            return true;
        }

        public static bool IsCompositeDigits(this long number) {
            while (number > 0) {
                if (!IsComposite(number % 10))
                    return false;
                number = number / 10;
            }

            return true;
        }

        public static bool IsMersennePrime(this long number) {
            return Array.BinarySearch<long>(Numbers.MersennePrimes, number) != -1;
        }
    }
}